﻿using Ionic.Zip;
using Microsoft.InformationProtectionAndControl;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace CloudStorageLight.Core.Web
{
    [Authorize]
    public abstract class WorkTaskControllerBase : Controller
    {
        protected virtual BlobUser GetCurrentUser()
        {
            var user = new BlobUser(System.Web.HttpContext.Current.User);
            return user;
        }

        protected virtual BlobUser GetWorkTaskUser()
        {
            if (_currentUser == null)
            {
                var workTicketCookie = Request.Cookies.Get(cookieName);
                if (workTicketCookie != null)
                {
                    var workTicket = FormsAuthentication.Decrypt(workTicketCookie.Value);
                    if (!workTicket.Expired)
                    {

                        _currentUser = new BlobUser(workTicket.Name);
                    }
                }
            }
            return _currentUser;
        }
        private BlobUser _currentUser;

        protected abstract BlobService CreateBlobService();


        public ActionResult ShowFileTransfer(string id)
        {
            var vm = new FileTransferVM();
            vm.WorkTaskId = Guid.NewGuid().ToString("d");
            vm.Expired = DateTime.Today.AddDays(10);
            vm.ReciveBaseDirectory = id;
            var link = Url.Action("FileTransfer", null, new { id = vm.WorkTaskId }, Request.Url.Scheme);
            vm.MailBody =  string.Format(Messages.MsgSendMailFileTransferBody, "", link);
            return View("_FileTransfer", vm);
        }

        [ValidateInput(false)]
        public async Task<ActionResult> SendFile(FileTransferVM vm)
        {
            if (string.IsNullOrEmpty(vm.To))
            {
                this.ModelState.AddModelError("", ViewMessages.MsgRequiredTo);
            }

            if (string.IsNullOrEmpty(vm.Title))
            {
                this.ModelState.AddModelError("", ViewMessages.MsgRequiredTitle);
            }

            if (vm.RemainderTimes.HasValue &&　(vm.RemainderTimes > 5 || vm.RemainderTimes <= 0))
            {
                this.ModelState.AddModelError("", ViewMessages.MsgInvaidReminderTimes);
            }


             if (!this.ModelState.IsValid)
             {
                 return View("_FileTransfer", vm);
             }

            var user = GetCurrentUser();
            var svc = CreateBlobService();
            var task = new FileTransferWorkTask(vm.WorkTaskId);
            task.From = user.Name;
            task.To = vm.To;
            task.Bcc = vm.Bcc;
            task.Password = vm.Password;
            task.MailBody = vm.MailBody;
            task.Title = vm.Title;
            task.FileReciveMode = vm.FileReciveMode.ToString();
            task.ReciveBaseDirectory = vm.ReciveBaseDirectory;
            task.Expired = TimeZoneInfo.ConvertTimeToUtc(vm.Expired, TimeZoneInfo.Local);
            task.ReminderTimes = vm.RemainderTimes;
 //           task.LinkAddress = Url.Action("FileTransfer", null, new { id = task.RowKey }, Request.Url.Scheme);
            task.SetFiles(vm.Files.Select(x=>HttpUtility.UrlDecode(x)));

            foreach(var mail in task.GetToUser())
            {
                if (!IsValidEmail(mail))
                {
                    this.ModelState.AddModelError("", string.Format(ViewMessages.MsgInvalidMailAddressPattern, mail));
                    break;
                }
            }

            if (this.ModelState.IsValid)
            {
                var impersonateUser = new BlobUser(task.GetImpersonationName());
                foreach (var file in task.GetFiles())
                {
                    if (await svc.GetProtectedModeAsync(file) != "None")
                    {
                        if (!await svc.CanControlAsync(impersonateUser, file))
                        {
                            throw new BlobException(BlobExceptionCode.NotAuthorization);
                        }
                    }
                }

                await svc.SendFilesMail(user, task);
                return Content("");
            }
            else
            {
                return View("_FileTransfer", vm);
            }



        }

        /// <summary>
        /// メールアドレスのパターンをチェック
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public static bool IsValidEmail(string email)
        {
            try
            {
                var addr = new System.Net.Mail.MailAddress(email);
                return true;
            }
            catch
            {
                return false;
            }
        }

        string cookieName = "WorkTaskTicket";


        [AllowAnonymous]
        public virtual async Task<ActionResult> FileTransfer(string id, string mailAddress, string password, int? fileNo)
        {
            using (var svc = CreateBlobService())
            {
                var workTask = svc.GetFileTransferWorkTask(id);
                if (workTask == null) return RedirectToAction("Index", "Home");

                var currentUser = GetCurrentUser();

                BlobUser impersonateUser;
                if (currentUser.IsGuest)
                {
                    var resultAction = AuthCheck(mailAddress, password, workTask);
                    if (resultAction != null) return resultAction;

                    var user = GetWorkTaskUser();
                    impersonateUser = new BlobUser(workTask.GetImpersonationName());
                    impersonateUser.OperatorName = user.Name;
                    impersonateUser.IsGuest = false;
                }
                else
                {
                    impersonateUser = currentUser;
                    impersonateUser.OperatorName = currentUser.Name;
                }


                var files = workTask.GetFiles();

                var userName = impersonateUser.OperatorName; //.Replace("@","_").Replace(".", "_");
                foreach (var ch in Path.GetInvalidFileNameChars())
                {
                    userName = userName.Replace(ch, '_');
                }

                if (Request.Files.Count > 0)
                {
                    if (string.IsNullOrEmpty(workTask.FileReciveMode) || workTask.FileReciveMode == FileReciveMode.None.ToString())
                    {
                        throw new BlobException(BlobExceptionCode.Prohibited);
                    }
                    return await UploadFiles(svc, workTask, impersonateUser, userName);

                }
                else if (fileNo.HasValue && fileNo.Value >= 0 && fileNo.Value < files.Count())
                {
                    var path = files[fileNo.Value];
                    return await DownloadFile(svc, workTask, impersonateUser, path);
                }
                else
                {
                    return await ShowWorkTask(svc, workTask, impersonateUser, files, userName);
                }

            }
        }

        private async Task<ActionResult> ShowWorkTask(BlobService svc, FileTransferWorkTask workTask, BlobUser impersonateUser, string[] files, string normalizedUserName)
        {
            var vm = new FileTransferWorkTaskVM(workTask, impersonateUser);
            foreach (var file in files)
            {
                try
                {
                    var blob = await svc.GetFileAsync(impersonateUser, file);
                    vm.DownloadableFiles.Add(new FileItem(blob));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }
            if (workTask.FileReciveMode == FileReciveMode.Current.ToString())
            {
                var uploadedFiles = await svc.ListFilesAsync(impersonateUser, workTask.ReciveBaseDirectory);
                var suffixUserName = "_" + normalizedUserName;
                foreach (var file in uploadedFiles)
                {
                    if (Path.GetFileNameWithoutExtension(file.Name).EndsWith(suffixUserName))
                    {
                        var item = new FileItem(file);
                        item.Title = Path.GetFileNameWithoutExtension(file.Name).Substring(0, Path.GetFileNameWithoutExtension(file.Name).Length - suffixUserName.Length) + Path.GetExtension(file.Name);
                        vm.UploadedFiles.Add(item);
                    }
                }
            }
            if (workTask.FileReciveMode == FileReciveMode.User.ToString())
            {
                var uploadedFiles = await svc.ListFilesAsync(impersonateUser, workTask.ReciveBaseDirectory + normalizedUserName +"/");
                foreach (var file in uploadedFiles.OfType<BlobFile>())
                {
                    vm.UploadedFiles.Add(new FileItem(file));
                }
            }
            return View(vm);
        }

        private async Task<ActionResult> UploadFiles(BlobService svc, FileTransferWorkTask workTask, BlobUser impersonateUser, string normalizedUserName)
        {
            var names = new List<string>();
            try
            {
                if (svc.CloudAccount.StorageLimitSize.HasValue)
                {
                    var size = WebUtil.GetUsedStoargeSize(svc.CloudAccount.AccountName);
                    if (size.HasValue && svc.CloudAccount.StorageLimitSize.Value < size.Value)
                    {
                        throw new BlobException(BlobExceptionCode.StorageQuotaExceeded);
                    }
                }

                for (int i = 0; i < Request.Files.Count; i++)
                {
                    var file = Request.Files[i];
                    var fileName = BlobUtil.UnescapeDataString(file.FileName);

                    var path = workTask.ReciveBaseDirectory + fileName;
                    var mode = (FileReciveMode)Enum.Parse(typeof(FileReciveMode), workTask.FileReciveMode);
                    if(mode == FileReciveMode.Current)
                    {
                        path = workTask.ReciveBaseDirectory + Path.GetFileNameWithoutExtension(file.FileName) + "_" + normalizedUserName + Path.GetExtension(file.FileName);
                    }
                    if (mode == FileReciveMode.User)
                    {
                        var dire = workTask.ReciveBaseDirectory + normalizedUserName + "/";
                        path = dire + fileName;
                        if (!await svc.ExistsAsync(dire)) await svc.CreateDirectoryAsync(impersonateUser, dire, impersonateUser.Name);
                    }                   
                    var uploaded = await svc.UploadFileAsync(impersonateUser, path, file.InputStream, file.ContentType);
                    var history = string.Format(Messages.MsgSendMailUploadedFileBody, path, impersonateUser.OperatorName, DateTime.Now);
                    svc.AddFileTransferHistory(workTask.Id, history, impersonateUser.OperatorName);

                }
                return Json(new { status = true, data = "" });
            }
            catch (Exception ex)
            {
                // svc.EntryLog(user.Name, "UploadFile Error", id, ex.ToString());
                return Json(new { status = false, data = ex.Message });
            }
        }

        private async Task<ActionResult> DownloadFile(BlobService svc, FileTransferWorkTask workTask, BlobUser impersonateUser, string path)
        {
            var blob = await svc.GetFileAsync(impersonateUser, path);
            if (await svc.GetProtectedModeAsync(path) != "None")
            {
                if (!await svc.CanControlAsync(impersonateUser, path))
                {
                    throw new BlobException(BlobExceptionCode.NotAuthorization);
                }
            }
            var fileName = BlobFile.GetFileName(path);
            Response.BufferOutput = false;
            Response.ContentType = "application/octet-stream";
            if (fileName.EndsWith(".html") || fileName.EndsWith(".htm")) Response.ContentType = "text/html";
            if (fileName.EndsWith(".txt")) Response.ContentType = "text/plain";

            string contentDisposition;
            if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
                contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
            else if (Request.Browser.Browser == "Safari")
                contentDisposition = "attachment; filename=" + fileName;
            else
                contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
            Response.AddHeader("Content-Disposition", contentDisposition);

            Response.SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" });
            await svc.DownloadFileAsync(impersonateUser, path, Response.OutputStream);
            var history = string.Format(Messages.MsgSendMailDownloadedFileBody, path, impersonateUser.OperatorName, DateTime.Now);
            svc.AddFileTransferHistory(workTask.Id, history, impersonateUser.OperatorName);
            return new EmptyResult();
        }

        private ActionResult AuthCheck(string mailAddress, string password, IWorkTask workTask)
        {
            bool auth = IsAuthUser(workTask);

            if (!auth)
            {
                var loginAction = Login(mailAddress, password, workTask);
                if (loginAction != null) return loginAction;
                var ticket = new FormsAuthenticationTicket(2, mailAddress, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(30), false, workTask.Id);
                Response.Cookies.Set(new HttpCookie(cookieName, FormsAuthentication.Encrypt(ticket)));
                return RedirectToAction(null);
            }
            return null;
        }

        private bool IsAuthUser(IWorkTask workTask)
        {
            bool auth = false;
            var workTicketCookie = Request.Cookies.Get(cookieName);
            if (workTicketCookie != null)
            {
                var workTicket = FormsAuthentication.Decrypt(workTicketCookie.Value);
                if (!workTicket.Expired && workTask.Id == workTicket.UserData)
                {
                    auth = true;
                }
            }
            return auth;
        }

        private ActionResult Login(string mailAddress, string password, IWorkTask worktask)
        {
            if (mailAddress == null) return View("UserLogin", worktask);

            if (!worktask.IsValidUser(mailAddress))
            {
                ModelState.AddModelError("", ViewMessages.MsgInvalidMailAddressForWorkTask);
                return View("UserLogin", worktask);
            }

            if (!string.IsNullOrEmpty(worktask.GetPassword()))
            {
                if (string.IsNullOrEmpty(password)) return View("UserLogin");
                if (worktask.GetPassword() != password)
                {
                    ModelState.AddModelError("", ViewMessages.MsgInvalidLoginUserPass);
                    return View("UserLogin", worktask);
                }
            }
            return null;
        }

        /// <summary>
        /// ZIPでダウンロード
        /// </summary>
        /// <param name="fc"></param>
        /// <returns></returns>
        [AllowAnonymous]
        public virtual async Task<ActionResult> DownloadAsZip(string id, FormCollection fc)
        {
            string tempzip = null;
            try
            {
                using (var svc = CreateBlobService())
                {
                    var workTask = svc.GetFileTransferWorkTask(id);
                    if (workTask == null) return RedirectToAction("Index", "Home");

                    var currentUser = GetCurrentUser();
                    BlobUser impersonateUser = null;
                    if (currentUser.IsGuest)
                    {
                        if (!IsAuthUser(workTask))
                        {
                            throw new BlobException(BlobExceptionCode.NotAuthorization);
                        }

                        var user = GetWorkTaskUser();
                        impersonateUser = new BlobUser(workTask.GetImpersonationName());
                        impersonateUser.OperatorName = user.Name;
                    }
                    else
                    {
                        impersonateUser = currentUser;
                        impersonateUser.OperatorName = currentUser.Name;
                    }

                    var files = workTask.GetFiles();

                    var pathList = new List<string>();
                    foreach (var key in fc.AllKeys)
                    {
                        if (!key.StartsWith("file")) continue;
                        var path = HttpUtility.UrlDecode(fc[key]);
                        if (!files.Contains(path))
                        {
                            throw new BlobException(BlobExceptionCode.NotAuthorization);
                        }
                        var blob = await svc.GetFileAsync(impersonateUser, path);
                        if (await svc.GetProtectedModeAsync(path) != "None")
                        {
                            if (!await svc.CanControlAsync(impersonateUser, path))
                            {
                                throw new BlobException(BlobExceptionCode.NotAuthorization);
                            }
                        }
                        pathList.Add(path);
                    }
                    tempzip = await WebUtil.CreateZipFile(svc, impersonateUser, pathList);

                    using (var source = new FileStream(tempzip, FileMode.Open))
                    {
                        Response.BufferOutput = false;
                        Response.SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" });
                        Response.ContentType = "application/octet-stream";
                        var strFileName = "downloadfiles.zip";
                        Response.AddHeader("Content-Disposition", "attachment; filename=" + strFileName);
                        await source.CopyToAsync(Response.OutputStream);
                    }


                    var sb = new List<string>();
                    foreach(var path in pathList)
                    {
                        var history = string.Format(Messages.MsgSendMailDownloadedFileBody, path, impersonateUser.OperatorName, DateTime.Now);
                        sb.Add(history);
                    }
                    svc.AddFileTransferHistory(workTask.Id, string.Join("\r\n", sb) , impersonateUser.OperatorName);
                }


                return new EmptyResult();

            }
            finally
            {
                if (tempzip != null && System.IO.File.Exists(tempzip)) System.IO.File.Delete(tempzip);
            }
        }

    }
}